package player;

import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Map;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.spi.AudioFileReader;

import org.tritonus.share.sampled.file.TAudioFileFormat;

import com.audiobuddy.serializables.Song;
import com.audiobuddy.serializables.SongCommand;

import domain.ServerDomainController;

public class MusicPlayer {
	
	private static final int STREAM_PORT = 8888;
	private ServerDomainController dc;
	private SourceDataLine dataLine;
	private MPListener listener;
	private MPlayer player;
	private Thread lthread;
	private Thread playerThread;
	private Song currentSong;
	 private static final int BUFFER_SIZE = 500000;

	public MusicPlayer(){
		
	}
	
	public MusicPlayer(ServerDomainController dc){
		this.dc = dc;
	}

	public void playNew(Song song) {
		//START PLAYING FROM BEGIN, NOT RESUME PLAYING AFTER PAUSE
		dc.requestStartStreamingSong(song);
		setCurrentSong(song);
		try {
			//Thread.sleep(500);
			
			File tempfile = File.createTempFile("mplayer", ".mp3", new File("C:\\"));
			
			listener = new MPListener(STREAM_PORT, tempfile, this);
			player = new MPlayer(listener, tempfile, this);
			lthread = new Thread(listener);
			lthread.start();
			playerThread = new Thread(player);
			playerThread.start();
			
			//play(tempfile);
			
		}  catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void stop(){
		//dataLine.stop();
		if(listener!=null && player!=null){
			listener.receiving = false;
			player.stop();
			lthread.interrupt();
			playerThread.interrupt();
		}
		//player.stop();
		
	}
	
	public void play(File infile){
		 System.out.println("Listener has started");
			try {
		           int c=0;
		           while (infile.length()<10000 &&c++<100)
		               Thread.currentThread().sleep(100);
		           
		           AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(infile);
		           AudioInputStream din = null;
		           AudioFormat baseFormat = audioInputStream.getFormat();
		           AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
							baseFormat.getSampleRate(),
							16,baseFormat.getChannels(),
							baseFormat.getChannels() * 2,
							baseFormat.getSampleRate(),
							false);
		           din = AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
					
		           Info info = new Info(SourceDataLine.class, decodedFormat);
		           dataLine = (SourceDataLine) AudioSystem.getLine(info);
		           dataLine.open(decodedFormat, BUFFER_SIZE);
		           dataLine.start();
		           int readBytes = 0;
		           byte[] buffer=new byte[BUFFER_SIZE];
		           while (readBytes != -1) {
				readBytes = din.read(buffer, 0, buffer.length);
				if (readBytes >= 0)
				    dataLine.write(buffer, 0, readBytes);
			    }
			} catch (SocketTimeoutException e) {
		           System.out.println("Player => Broadcast seems to have been stopped");
		           dataLine.stop();
		       } catch(Exception e) {
			    System.out.println(String.format("Listener error : %s",e));
		           e.printStackTrace();
		           dataLine.stop();
			    return;
			}
	}

	public InetAddress getBroadcasterIp() {
		if(listener!=null){
			return listener.broadcaster;
		}else{
			return null;
		}
	}

	public Song getCurrentSong() {
		return currentSong;
	}

	public void setCurrentSong(Song currentSong) {
		this.currentSong = currentSong;
		dc.setCurrentSong(currentSong);
	}

	public void next() {
		dc.executeSongCommand(new SongCommand(SongCommand.NEXT,0));
		
	}

	public void setVolume(int value) {
		//DOES NOT WORK YET 
		Port speaker;
		FloatControl volume;
		try{
			Mixer mixer = AudioSystem.getMixer(null);
			speaker = (Port) mixer.getLine(Port.Info.LINE_OUT);
			speaker.open();
			volume = (FloatControl) speaker.getControl(FloatControl.Type.VOLUME);
			volume.setValue((float)value);
			System.out.println("volume= " +volume.getValue() + ", max=" + volume.getMaximum());
		}catch (Exception e) {
			System.out.println("Error volume: "+e);
		}/*
		
		DataLine line = player.dataLine;
		if(player.dataLine.isControlSupported(FloatControl.Type.VOLUME)){
			
			volume = (FloatControl) player.dataLine.getControl(FloatControl.Type.VOLUME);
			volume.setValue((float) value);
			System.out.println("volume= " +volume.getValue() + ", max=" + volume.getMaximum());
		}else{
			System.out.println("volume control not supported");
		}*/
		
	}
}
	
class MPListener implements Runnable{
		   private static final int BUFFER_SIZE=800000;
		   private int port;
		   private DatagramSocket socket;
		   private File outfile;
		   public boolean receiving;
		   public InetAddress broadcaster;
		   private MusicPlayer parentPlayer;
		   
		   public MPListener(int port, File outfile, MusicPlayer parentPlayer) {
		       this.port=port;
		       this.outfile=outfile;
		       receiving = true;
		       this.parentPlayer = parentPlayer;
		       System.out.println("new listener");
		   }
		   
		   
		@Override
		public void run() {
			FileOutputStream stream = null;
			try{
				stream = new FileOutputStream(outfile);
				socket = new DatagramSocket(port);
				socket.setSoTimeout(2000);//wait a seconds to start
				socket.setReceiveBufferSize(BUFFER_SIZE);
				byte[] receiveBuffer = new byte[BUFFER_SIZE];
				while(receiving){
					DatagramPacket receivePacket=new DatagramPacket(receiveBuffer, receiveBuffer.length);
		               try {
		                   socket.receive(receivePacket);
		                   broadcaster = receivePacket.getAddress();
		               } catch (SocketTimeoutException e) {
		                   System.out.println("Broadcast seems to have been stopped");
		                   receiving = false;
		                   stream.close();
		                   socket.close();
		                   break;
		               }
		               stream.write(receiveBuffer,receivePacket.getOffset(),receivePacket.getLength());
		               // System.out.println("wrote to stream");
		               socket.setSoTimeout(1000);//be less patient once started
				}
				if(!receiving){
					stream.close();
	                socket.close();
					System.out.println("stopped receiving");
					System.out.println("received: " +outfile.length()/1024 + "KB/"+ parentPlayer.getCurrentSong().getFilesize()/1024+"KB" );
					if(outfile.length()>=parentPlayer.getCurrentSong().getFilesize()){
						//volledig overgezet
						
					}
				}
			}catch(Exception e) {
			    System.out.println(String.format("MPListener error : %s",e));
		           e.printStackTrace();
			    return;
			}finally{
				socket.close();
				broadcaster = null;
				System.out.println("closed receiving socket");
			}
			
		}
	}
	
	
	
	
	
	
class MPlayer  implements Runnable{
		   private MPListener mplistener;
		   private File infile;
		   public SourceDataLine dataLine;
		private MusicPlayer parentPlayer;
		private boolean stoppedByUser;
		   private static final int BUFFER_SIZE = 500000;
		   int extraAttempt = 0;
		   
		   
		   
		   public MPlayer (MPListener mpl, File infile, MusicPlayer parentPlayer) {
			this.mplistener=mpl;
		       this.infile=infile;
		       System.out.println("new player");
		       this.parentPlayer = parentPlayer;
		   }
		   
		   public void stop(){
			   if(dataLine!=null){
			   dataLine.stop();
			   dataLine.drain();
			   dataLine.close();}
			   stoppedByUser = true;
		   }
		   
		   

		   public void run() {
		       System.out.println("Listener has started");
			try {
				stoppedByUser = false;
		           int c=0;
		           while (infile.length()<20000 && c++<100)
		               Thread.currentThread().sleep(100);
		           AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(infile);
		           AudioInputStream din = null;
		           AudioFormat baseFormat = audioInputStream.getFormat();
		           AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
							baseFormat.getSampleRate(),
							16,baseFormat.getChannels(),
							baseFormat.getChannels() * 2,
							baseFormat.getSampleRate(),
							false);
		           din = AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
		           
		           Info info = new Info(SourceDataLine.class, decodedFormat);
		           dataLine = (SourceDataLine) AudioSystem.getLine(info);
		           
		           dataLine.open(decodedFormat, BUFFER_SIZE);
		           dataLine.start();
		           int readBytes = 0;
		           byte[] buffer=new byte[BUFFER_SIZE];
		           while (readBytes != -1) {
				readBytes = din.read(buffer, 0, buffer.length);
				//System.out.println("dataline start din.read");
				if (readBytes >= 0)
				    dataLine.write(buffer, 0, readBytes);
					//System.out.println("dataline.write: " + dataLine.getMicrosecondPosition()/1000000 + "/" + dataLine.getBufferSize());
			    }
		           
		           //play next song at the end.
		           //Still contains some errors, like when stop is pressed, it will play next...
		           
		           if(readBytes == -1 && !stoppedByUser){
		           	
		        	  dataLine.stop();
		        	  parentPlayer.next();
		        	  
		           }
			} catch (SocketTimeoutException e) {
		           System.out.println("Player => Broadcast seems to have been stopped");
		           dataLine.stop();
		       } catch(UnsupportedAudioFileException e){
		    	   //needs some extra time sometimes
		    	   
		    	   
		    	   if(extraAttempt<4){
		    		   extraAttempt++;
		    	   		run();
		    	   		try {
							Thread.sleep(300);
						} catch (InterruptedException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
		    	   }
		       }
			catch(Exception e) {
			    System.out.println(String.format("Listener error : %s",e));
		           e.printStackTrace();
		           dataLine.stop();
			    return;
			}
		   }
}
